-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Adds Raycaster with tracking for Dynamic Meshes #3298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Adds Raycaster with tracking for Dynamic Meshes #3298
Conversation
Add the efficient multi-mesh raycasting function implemented in Orbit. Moreover, this PR fixes the test of it. The new raycaster allows to raycast against multiple objects, which can be located at different positions in each environment. The positions can be tracked over time if enabled in the config. - New feature (non-breaking change which adds functionality) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Co-authored-by: zrene <[email protected]>
…ssion (isaac-sim#48) Fixes number of meshes in the `RayCaster` when raycasting dynamically against a regex expression of multiple objects in the scene. - Bug fix (non-breaking change which fixes an issue) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
Change Mulit-mesh raycaster and raycaster camera to own files, restore the ones of main to simplify the merge. NOTE: test of the camera is currently failing, similar as on public main at that time, should be fixed after update to latest main - Breaking change (fix or feature that would cause existing functionality to not work as expected) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [ ] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
…d fixes tests (isaac-sim#65) ClassVar not correctly destroyed, thus removing it (follow changes of original RayCaster). Fixing tests to comply with changes of our internal code with 1.4.1. - Bug fix (non-breaking change which fixes an issue) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Co-authored-by: zrene <[email protected]>
…_meshes for now
…nstances to benchmark. Fix callback issues for mulit mesh
|
Thanks for this feature! I've noticed that the duplicate mesh detection in Currently, A more efficient alternative could be to use a hash-based lookup table to detect mesh duplicates. We can assign a hash key to each mesh based on its vertices and use it for fast duplicate checks. This would reduce the overall complexity to O(n * V) for the entire duplicate mesh detection process. Example implementation: def _get_mesh_key(vertices: np.ndarray) -> tuple[int, int, bytes]:
"""Build a key from the shape and data hash of a mesh vertex array."""
data = np.ascontiguousarray(vertices).view(np.uint8) # Ensure array is contiguous
h = hashlib.blake2b(data, digest_size=16)
return (vertices.shape[0], vertices.shape[1], h.digest())And in def _initialize_warp_meshes(self):
...
for target_cfg in self._raycast_targets_cfg:
...
registered_meshes: dict[tuple[int, int, bytes], int] = {} # Maps mesh keys to wp_mesh indices
wp_mesh_ids = []
for target_prim in target_prims:
...
if str(target_prim.GetPath()) in MultiMeshRayCaster.meshes:
wp_mesh_ids.append(MultiMeshRayCaster.meshes[str(target_prim.GetPath())].id)
continue
...
mesh_key = _get_mesh_key(trimesh_mesh.vertices)
registered_idx = registered_meshes.get(mesh_key, -1)
if registered_idx != -1 and self.cfg.reference_meshes:
omni.log.info("Found a duplicate mesh, only reference the mesh.")
wp_mesh_ids.append(wp_mesh_ids[registered_idx])
else:
wp_mesh = convert_to_warp_mesh(trimesh_mesh.vertices, trimesh_mesh.faces, device=self.device)
MultiMeshRayCaster.meshes[str(target_prim.GetPath())] = wp_mesh
wp_mesh_ids.append(wp_mesh.id)
registered_meshes[mesh_key] = len(wp_mesh_ids) - 1 # Store wp_mesh idxThis approach should reduce initialization time significantly when processing multiple meshes (from O(n² * V) to O(n * V)). |
|
Thanks a lot for the valuable feedback. Hashing the vertices is a great idea to speed up retrieval. I’ll incorporate this, along with an additional check for potential hash collisions, once I find the time. Also, note that cache lookups can be further accelerated by setting the is_shared flag in the configuration to true. This assumes that all environments share the same meshes, avoiding redundant checking for each one. |
|
This is definitely useful feedback @pavelacamposp @renezurbruegg I suggest though that we keep this MR to the current limitation and make a separate one with the hashing implementation. Just to not have this MR hanging around for too long :) |
Thanks a lot for the input. I added all prim types now and verified that they work correctly: |
Thanks, Updated it with your generic fan triangulation |
|
@renezurbruegg @pascal-roth Should we review this again and then prepare it for merge? |
Yes 👍 There is one remaining comment from @ooctipus, which ideally would be resolved in this pr #3371 |
|
@renezurbruegg we should probably do a quick profiling with tools like Nsight Systems or with Warp's ScopedTimer (https://nvidia.github.io/warp/profiling.html) to know our bottleneck for large resolutions, then we can also think about improving our kernel (maybe also for a next version) |
# Description This MR adds two functions to obtain the pose and scale of a prim respectively. This is needed for #3298. ## Type of change - New feature (non-breaking change which adds functionality) - This change requires a documentation update ## Checklist - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Signed-off-by: Mayank Mittal <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Kelly Guo <[email protected]>
|
line 166 in But I don't see is_global defined in target_cfg? it should be changed to am I right? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Overview
Greptile Summary
This PR introduces MultiMeshRayCaster and MultiMeshRayCasterCamera to enable raycasting against multiple dynamic meshes. The key architectural changes include:
-
New sensor classes -
MultiMeshRayCasterandMultiMeshRayCasterCameraextend the baseRayCaster/RayCasterCamerawith support for tracking moving mesh transforms (robot parts, articulated bodies). -
Core infrastructure - Refactors
RayCasterto use class-level mesh caching, adds low-level warp kernels (raycast_static_meshes_kernel,raycast_dynamic_meshes_kernel), and introduces mesh utility functions for USD-to-trimesh conversion. -
Configuration API - New
MultiMeshRayCasterCfg.RaycastTargetCfgallows per-mesh configuration of tracking behavior, sharing, and merging options. The API maintains backward compatibility by converting strings to default configurations. -
Memory optimization - Implements mesh deduplication across environments to reduce memory footprint.
The refactoring moves mesh storage from instance to class level and extracts pose computation logic into reusable utilities (sim_utils.obtain_world_pose_from_view()), enabling dynamic mesh tracking while maintaining API compatibility.
Critical Issues Requiring Attention:
-
Race condition in warp kernels (kernels.py lines 135-155, 221-242): Multiple threads may simultaneously check
if mesh_query_ray_t.t < ray_distance[tid_env, tid_ray]and write results non-atomically. This could lead to non-deterministic hit results when multiple meshes intersect the same ray. Consider using atomic operations or mesh-ordered processing. -
Test failures likely (test_multi_mesh_ray_caster_camera.py lines 438, 534): Prim path mismatches where camera config references
"/World/Camera"but the prim is created at"/World/Camera_warp". These tests will fail at runtime. -
Gymnasium major version bump (extension.toml line 20): Upgrading from 0.29.0 to 1.2.0 is a breaking change unrelated to the raycaster feature. This should be split into a separate PR to avoid introducing unexpected API breakage.
-
Shared state pollution risk (multi_mesh_ray_caster.py line 76): Class-level
mesh_offsetsdict is mutable and shared across all instances. If different instances use the same prim path with different configurations, they may overwrite each other's data. -
Device mismatch bug (test_multi_mesh_ray_caster.py line 197):
cube_rotationtensor created without device specification will default to CPU, causing device mismatch errors when used with GPU tensors. -
Documentation error (multi_mesh_ray_caster_cfg.py line 58): References non-existent
is_globalattribute instead ofis_shared, misleading users about the API.
Confidence: 2/5 - Multiple critical bugs (race conditions, test failures, device mismatches) and the unrelated Gymnasium version change significantly increase merge risk.
23 files reviewed, 16 comments
| "toml", | ||
| "hidapi", | ||
| "gymnasium==0.29.0", | ||
| "gymnasium==1.2.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Major version bump from gymnasium 0.29.0to 1.2.0 - this is a breaking change in the API. The Gymnasium 1.x release introduced significant API changes including removal of deprecated features and changes to environment initialization. Have all existing environments and RL training scripts been tested with Gymnasium 1.2.0 to ensure compatibility?
| from .multi_mesh_ray_caster import MultiMeshRayCaster | ||
| from .multi_mesh_ray_caster_camera import MultiMeshRayCasterCamera | ||
| from .multi_mesh_ray_caster_camera_cfg import MultiMeshRayCasterCameraCfg | ||
| from .multi_mesh_ray_caster_cfg import MultiMeshRayCasterCfg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: missing data class imports for the multi-mesh ray caster (e.g., MultiMeshRayCasterData, MultiMeshRayCasterCameraData). Are the data classes for MultiMeshRayCaster intentionally excluded from this module's public API, or should they be exported?
| ) | ||
| prim_utils.create_prim("/World/Camera_warp", "Xform") | ||
| camera_cfg_warp = MultiMeshRayCasterCameraCfg( | ||
| prim_path="/World/Camera", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: prim_path mismatch: camera_warp uses /World/Camera but prim created at /World/Camera_warp - likely causes initialization to fail
| ) | ||
| prim_utils.create_prim("/World/Camera_warp", "Xform") | ||
| camera_cfg_warp = MultiMeshRayCasterCameraCfg( | ||
| prim_path="/World/Camera", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: prim_path mismatch: camera_warp uses /World/Camera but prim created at /World/Camera_warp
| if not triggered: | ||
| if countdown > 0: | ||
| countdown -= 1 | ||
| continue | ||
| data = scene["ray_caster"].data.ray_hits_w.cpu().numpy() # noqa: F841 | ||
| triggered = True | ||
| else: | ||
| continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: dead code - triggered starts as True, so this block never executes and the countdown mechanism is unreachable
| ray_face_id_wp = wp.empty((1, 1), dtype=wp.int32, device=torch_device) | ||
|
|
||
| if return_mesh_id: | ||
| ray_mesh_id = -torch.ones((n_envs, n_rays_per_env), dtype=torch.int16, device=torch_device).contiguous() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: initializes with negative values, but the docstring (line 232) says missed hits return 0. This inconsistency could cause incorrect results.
| """ | ||
| self._check_supported_data_types(cfg) | ||
| # initialize base class | ||
| MultiMeshRayCaster.__init__(self, cfg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Only MultiMeshRayCaster.__init__ is called explicitly. If RayCasterCamera has initialization logic, it will be skipped, potentially causing incomplete setup. Should both parent class constructors be called via super() or using cooperative multiple inheritance pattern?
| self.ray_hits_w, ray_depth, ray_normal, _, ray_mesh_ids = raycast_dynamic_meshes( | ||
| self._ray_starts_w[env_ids], | ||
| self._ray_directions_w[env_ids], | ||
| mesh_ids_wp=self._mesh_ids_wp, # list with shape num_envs x num_meshes_per_env | ||
| max_dist=self.cfg.max_distance, | ||
| mesh_positions_w=self._mesh_positions_w[env_ids], | ||
| mesh_orientations_w=self._mesh_orientations_w[env_ids], | ||
| return_distance=any( | ||
| [name in self.cfg.data_types for name in ["distance_to_image_plane", "distance_to_camera"]] | ||
| ), | ||
| return_normal="normals" in self.cfg.data_types, | ||
| return_mesh_id=self.cfg.update_mesh_ids, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: The raycast call uses env_ids indexing for ray data but env_ids_tensor for output buffers (lines201, 205,209, 212). This inconsistency could cause indexing mismatches if env_ids is not a tensor. Should the raycast call on line 177 use env_ids_tensor instead of env_ids for consistency?
| if mesh_query_ray_t.result: | ||
|
|
||
| # check if hit distance is less than the current hit distance, only then update the memory | ||
| if mesh_query_ray_t.t < ray_distance[tid_env, tid_ray]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: race condition possible - multiple meshes can hit the same ray simultaneously. if two threads update ray_distance[tid_env, tid_ray] concurrently before reading it, both may pass the check and overwrite each other's results non-deterministically. are the kernel launches configured to ensure tid_mesh_id iterations happen sequentially for each (tid_env, tid_ray) pair?
| if mesh_query_ray_t.result: | ||
|
|
||
| # check if hit distance is less than the current hit distance, only then update the memory | ||
| if mesh_query_ray_t.t < ray_distance[tid_env, tid_ray]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: same race condition as raycast_static_meshes_kernel line 141 - concurrent writes to ray_distance. are the kernel launches configured to ensure tid_mesh_id iterations happen sequentially for each (tid_env, tid_ray) pair?
…c-sim#3371) # Description This MR adds two functions to obtain the pose and scale of a prim respectively. This is needed for isaac-sim#3298. ## Type of change - New feature (non-breaking change which adds functionality) - This change requires a documentation update ## Checklist - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Signed-off-by: Mayank Mittal <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Kelly Guo <[email protected]>
) # Description This MR does the following: * Adds parsing of instanced prims in `isaaclab.sim.utils.get_all_matching_child_prims` and `isaaclab.sim.utils.get_first_matching_child_prim`. Earlier, instanced prims were skipped since `Usd.Prim.GetChildren` does not return instanced prims. * Adds parsing of instanced prims in `isaaclab.sim.utils.make_uninstanceable` to make all prims uninstanceable. These are needed to parse meshes for the dynamic raycaster class in isaac-sim#3298 ## Type of change - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) ## Checklist - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Signed-off-by: Mayank Mittal <[email protected]> Co-authored-by: Kelly Guo <[email protected]>
…c-sim#3371) # Description This MR adds two functions to obtain the pose and scale of a prim respectively. This is needed for isaac-sim#3298. ## Type of change - New feature (non-breaking change which adds functionality) - This change requires a documentation update ## Checklist - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Signed-off-by: Mayank Mittal <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Kelly Guo <[email protected]>
…c-sim#3371) This MR adds two functions to obtain the pose and scale of a prim respectively. This is needed for isaac-sim#3298. - New feature (non-breaking change which adds functionality) - This change requires a documentation update - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Signed-off-by: Mayank Mittal <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Kelly Guo <[email protected]>

Description
This PR introduces
MultiMeshRayCasterandMultiMeshRayCasterCamera, an extension of the defaultRayCasterwith the following enhancements:This is joint work with @pascal-roth and @Mayankm96.
The default
RayCasterwas limited to static environments and required manual handling of moving meshes, which restricted its use for robotics scenarios where robots or obstacles move dynamically.MultiMeshRayCasteraddresses these limitations by and now supports raycasting against robot parts and other moving entities.Usage
For a quick demo, run:
Drop-in replacement
Example change to migrate from
RayCasterCfgtoMultiMeshRayCasterCfg:Benchmarking & Validation
To benchmark the new raycaster, run:
Then plot the results with:
This will generate outputs under:
outputs/benchmarks/raycast_benchmark...Example plots
Type of Change
Checklist
pre-commitchecks with./isaaclab.sh --formatconfig/extension.tomlfileCONTRIBUTORS.mdor my name already exists there